package com.ruoyix.flowable.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyix.common.core.domain.entity.SysUser;
import com.ruoyix.common.helper.LoginHelper;
import com.ruoyix.common.utils.StringUtils;
import com.ruoyix.common.core.page.TableDataInfo;
import com.ruoyix.common.core.domain.PageQuery;
import com.ruoyix.flowable.domain.BpmTaskExt;
import com.ruoyix.flowable.domain.vo.BpmTaskApproveVo;
import com.ruoyix.flowable.domain.vo.BpmTaskAssigneeVo;
import com.ruoyix.flowable.enums.TaskStatus;
import com.ruoyix.flowable.mapper.BpmTaskExtMapper;
import com.ruoyix.flowable.service.IBpmTaskAssigneeService;
import com.ruoyix.flowable.util.FlowableUtil;
import com.ruoyix.system.mapper.SysUserMapper;
import org.flowable.bpmn.model.*;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import lombok.RequiredArgsConstructor;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.stereotype.Service;
import com.ruoyix.flowable.domain.bo.BpmTaskBo;
import com.ruoyix.flowable.domain.vo.BpmTaskVo;
import com.ruoyix.flowable.service.IBpmTaskService;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程任务信息Service业务层处理
 *
 * @author ruoyi
 * @date 2023-11-27
 */
@RequiredArgsConstructor
@Service
public class BpmTaskServiceImpl implements IBpmTaskService {

    private final TaskService taskService;
    private final HistoryService historyService;
    private final RuntimeService runtimeService;
    private final IBpmTaskAssigneeService iBpmTaskAssigneeService;
    private final SysUserMapper userMapper;
    private final BpmTaskExtMapper bpmTaskExtMapper;
    private final RepositoryService repositoryService;




    /**
     * 查询流程任务信息列表
     */
    @Override
    public TableDataInfo<BpmTaskVo> queryPageList(BpmTaskBo bo, PageQuery pageQuery) {
        List<BpmTaskVo> result = new ArrayList<>();
        TaskQuery taskQuery = taskService.createTaskQuery()
            .taskAssignee(Objects.requireNonNull(LoginHelper.getUserId()).toString())
            .active();  // 只查询活动的任务（未完成的任务）

        if (bo.getName() != null) {
            taskQuery.taskName(bo.getName());
        }

        List<Task> taskList = taskQuery
            .orderByTaskCreateTime()
            .desc()
            .listPage((pageQuery.getPageNum() - 1) * pageQuery.getPageSize(), pageQuery.getPageSize() * pageQuery.getPageNum());

        TaskQuery countQuery = taskService.createTaskQuery()
            .taskAssignee(Objects.requireNonNull(LoginHelper.getUserId()).toString())
            .active();

        if (bo.getName() != null) {
            countQuery.taskName(bo.getName());
        }

        long count = countQuery.count();

        taskList.forEach(item -> {
            BpmTaskVo bpmTaskVo = new BpmTaskVo();
            bpmTaskVo.setId(item.getId());
            bpmTaskVo.setName(item.getName());
            bpmTaskVo.setStartTime(item.getCreateTime());
            bpmTaskVo.setEndTime(item.getClaimTime());
            bpmTaskVo.setProcessInstanceId(item.getProcessInstanceId());
            bpmTaskVo.setExecutionId(item.getExecutionId());
            bpmTaskVo.setProcessDefinitionId(item.getProcessDefinitionId());
            bpmTaskVo.setAssignee(item.getAssignee());
            bpmTaskVo.setAssigneeId(item.getAssignee());
            bpmTaskVo.setProcessDefinitionKey(item.getTaskDefinitionKey());
            bpmTaskVo.setProcessDefinitionName(item.getTaskDefinitionKey());
            result.add(bpmTaskVo);
        });
        TableDataInfo<BpmTaskVo> build = TableDataInfo.build(result);
        build.setTotal(count);
        return build;
    }





    @Override
    public TableDataInfo<BpmTaskVo> finishList(BpmTaskBo bo, PageQuery pageQuery) {
        List<BpmTaskVo> result = new ArrayList<>();

        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
            .finished()
            .taskAssignee(Objects.requireNonNull(LoginHelper.getUserId()).toString());

        if (bo.getName() != null) {
            historicTaskInstanceQuery.taskName(bo.getName());
        }

        List<HistoricTaskInstance> taskList = historicTaskInstanceQuery
            .orderByTaskCreateTime()
            .desc()
            .listPage((pageQuery.getPageNum() - 1) * pageQuery.getPageSize(), pageQuery.getPageSize() * pageQuery.getPageNum());

        HistoricTaskInstanceQuery countQuery = historyService.createHistoricTaskInstanceQuery()
            .finished()
            .taskAssignee(Objects.requireNonNull(LoginHelper.getUserId()).toString());

        if (bo.getName() != null) {
            countQuery.taskName(bo.getName());
        }

        long count = countQuery.count();


        taskList.forEach(item -> {
            BpmTaskVo bpmTaskVo = new BpmTaskVo();
            bpmTaskVo.setId(item.getId());
            bpmTaskVo.setName(item.getName());
            bpmTaskVo.setStartTime(item.getCreateTime());
            bpmTaskVo.setEndTime(item.getEndTime());
            bpmTaskVo.setProcessInstanceId(item.getProcessInstanceId());
            bpmTaskVo.setExecutionId(item.getExecutionId());
            bpmTaskVo.setProcessDefinitionId(item.getProcessDefinitionId());
            bpmTaskVo.setAssignee(item.getAssignee());
            bpmTaskVo.setAssigneeId(item.getAssignee());
            bpmTaskVo.setProcessDefinitionKey(item.getTaskDefinitionKey());
            bpmTaskVo.setProcessDefinitionName(item.getTaskDefinitionKey());
            result.add(bpmTaskVo);
        });
        TableDataInfo<BpmTaskVo> build = TableDataInfo.build(result);
        build.setTotal(count);
        return build;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean complete(BpmTaskApproveVo bpmTaskApproveVo) {

        //查询对应task信息
        Task task = taskService.createTaskQuery().taskId(bpmTaskApproveVo.getTaskId()).singleResult();
        if(ObjectUtil.isEmpty(task)){
            throw new RuntimeException("流程任务不存在");
        }
        if(!Objects.requireNonNull(LoginHelper.getUserId()).toString().equals(task.getAssignee())){
            throw new RuntimeException("您不是当前任务审批人");
        }


        //保存流程任务扩展信息
        BpmTaskExt bpmTaskExt = new BpmTaskExt();
        bpmTaskExt.setId(task.getId());
        bpmTaskExt.setName(task.getName());
        bpmTaskExt.setStartTime(task.getCreateTime());
        bpmTaskExt.setEndTime(task.getClaimTime());
        bpmTaskExt.setProcessInstanceId(task.getProcessInstanceId());
        bpmTaskExt.setExecutionId(task.getExecutionId());
        bpmTaskExt.setProcessDefinitionId(task.getProcessDefinitionId());
        bpmTaskExt.setAssigneeId(task.getAssignee());
        String assigneeId = task.getAssignee();
        if (assigneeId != null) {
            SysUser sysUser = userMapper.selectUserById(Long.valueOf(task.getAssignee()));
            bpmTaskExt.setAssignee(sysUser == null ? null : sysUser.getNickName());
        }
        bpmTaskExt.setAssignee(task.getAssignee());
        bpmTaskExt.setProcessDefinitionKey(task.getTaskDefinitionKey());
        bpmTaskExt.setProcessDefinitionName(task.getTaskDefinitionKey());

        bpmTaskExt.setFormFields(bpmTaskApproveVo.getFormFields());
        bpmTaskExt.setFormOption(bpmTaskApproveVo.getFormOption());
        bpmTaskExt.setFormVariables(bpmTaskApproveVo.getFormVariables());
        bpmTaskExt.setFormCustomCreatePath(bpmTaskApproveVo.getFormCustomCreatePath());
        bpmTaskExt.setFormType(bpmTaskApproveVo.getFormType());
        bpmTaskExtMapper.insert(bpmTaskExt);

        Map<String, Object> variables = JSONUtil.parseObj(bpmTaskApproveVo.getFormVariables());
        taskService.setVariables(task.getId(),variables);
        taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatus.COMPLETED.getTaskStatus(), bpmTaskApproveVo.getFlowData().getComment());
        taskService.complete(task.getId(), taskService.getVariables(task.getId()));



        //查询下一节点task信息,设置审批人
        Task nextTask = taskService.createTaskQuery()
            .processInstanceId(task.getProcessInstanceId())
            .singleResult();
        if(ObjectUtil.isNotEmpty(nextTask)){
            BpmTaskAssigneeVo bpmTaskAssigneeVo = iBpmTaskAssigneeService.selectByDefineIdAndTaskKey(nextTask.getProcessDefinitionId(), nextTask.getTaskDefinitionKey());
            if(ObjectUtil.isEmpty(bpmTaskAssigneeVo)){
                throw new RuntimeException("流程定义信息不存在");
            }
            if (StringUtils.isNotBlank(bpmTaskApproveVo.getFlowData().getAssignee())) {
                taskService.setAssignee(nextTask.getId(),bpmTaskApproveVo.getFlowData().getAssignee());
            } else {
                String[] stringArray = bpmTaskAssigneeVo.getAssigneeId().split(",");
                // 从数组中随机选择一个元素
                String randomUserId = getRandomElement(stringArray);
                taskService.setAssignee(nextTask.getId(),randomUserId);
            }
        }
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean reject(String taskId,String comment) {

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if(ObjectUtil.isEmpty(task)){
            throw new RuntimeException("流程任务不存在");
        }
        taskService.addComment(taskId, task.getProcessInstanceId(), TaskStatus.REJECTED.getTaskStatus(), comment);
        runtimeService.deleteProcessInstance(task.getProcessInstanceId(), "Rejected by User");
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BpmTaskApproveVo getNextNodeMessage(String taskId) {


        // 获取任务节点
        Task task = taskService.createTaskQuery()
            .taskId(taskId)
            .singleResult();

        //查询下一节点流程定义设置信息
        List<FlowElement> nextNodeList = FlowableUtil.getNextNodeList(task.getProcessInstanceId(), task.getId());
        List<BpmTaskAssigneeVo> bpmTaskAssigneeVoList = new ArrayList<>();
        for (FlowElement flowElement : nextNodeList) {
            BpmTaskAssigneeVo bpmTaskAssigneeVo = iBpmTaskAssigneeService.selectByDefineIdAndTaskKey(task.getProcessDefinitionId(), flowElement.getId());
            //判断是否弹框选人是否
            if (bpmTaskAssigneeVo.getDialogAssignee() == 1L) {
                if (StringUtils.isNotBlank(bpmTaskAssigneeVo.getAssigneeId())) {
                    List<String> list = Arrays.asList(bpmTaskAssigneeVo.getAssigneeId().split(","));
                    List<Long> userIdList = new ArrayList<>();
                    for (String str : list) {
                        userIdList.add(Long.parseLong(str));
                    }
                    List<SysUser> sysUsers = userMapper.selectBatchIds(userIdList);
                    bpmTaskAssigneeVo.setUserList(sysUsers);
                }
                bpmTaskAssigneeVoList.add(bpmTaskAssigneeVo);
            }
        }
        BpmTaskApproveVo bpmTaskApproveVo = new BpmTaskApproveVo();
        bpmTaskApproveVo.setBpmTaskAssigneeVoList(bpmTaskAssigneeVoList);
        bpmTaskApproveVo.setTaskId(task.getId());
        return bpmTaskApproveVo;
    }


    private static String getRandomElement(String[] array) {
        // 使用Random类生成随机索引
        Random random = new Random();
        int randomIndex = random.nextInt(array.length);

        // 返回随机选择的元素
        return array[randomIndex];
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delegateTask(BpmTaskApproveVo bpmTaskApproveVo) {
        Task task = taskService.createTaskQuery().taskId(bpmTaskApproveVo.getTaskId()).singleResult();
        if(ObjectUtil.isEmpty(task)){
            throw new RuntimeException("任务信息不存在");
        }
        SysUser targetUser = userMapper.selectUserById(bpmTaskApproveVo.getAssigneeUserId());
        SysUser fromUser = userMapper.selectUserById(LoginHelper.getUserId());
        taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatus.DELEGATED.getTaskStatus(),"委派："+targetUser.getNickName()+"->"+fromUser.getNickName());
        taskService.delegateTask(task.getId(),bpmTaskApproveVo.getAssigneeUserId().toString());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean transferTask(BpmTaskApproveVo bpmTaskApproveVo) {
        Task task = taskService.createTaskQuery().taskId(bpmTaskApproveVo.getTaskId()).singleResult();
        if(ObjectUtil.isEmpty(task)){
            throw new RuntimeException("任务信息不存在");
        }
        SysUser targetUser = userMapper.selectUserById(bpmTaskApproveVo.getAssigneeUserId());
        SysUser fromUser = userMapper.selectUserById(LoginHelper.getUserId());
        taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatus.TRANSFER.getTaskStatus(),"转办："+targetUser.getNickName()+"->"+fromUser.getNickName());
        taskService.setAssignee(task.getId(),bpmTaskApproveVo.getAssigneeUserId().toString());
        return true;
    }

    @Override
    public void returnTask(BpmTaskApproveVo bpmTaskApproveVo) {

    }


    /**
     * 自由流任务
     *
     * @param bpmTaskApproveVo     流程信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean freeFlowTask(BpmTaskApproveVo bpmTaskApproveVo) {


        // 获取当前任务
        Task currentTask = taskService.createTaskQuery().taskId(bpmTaskApproveVo.getTaskId()).singleResult();

        List<HistoricTaskInstance> historicTasks = historyService.createHistoricTaskInstanceQuery()
            .processInstanceId(currentTask.getProcessInstanceId())
            .orderByHistoricTaskInstanceStartTime()
            .asc()
            .list();

        //查找historicTasks中taskDefinitionKey与bpmTaskApproveVo.getTargetActivityId()相同的task
        String assignee = null;
        for (HistoricTaskInstance historicTask : historicTasks) {
            if (historicTask.getTaskDefinitionKey().equals(bpmTaskApproveVo.getTargetActivityId())) {
                assignee = historicTask.getAssignee();
                break;
            }
        }

        // 获取流程模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId());

        // 获取当前节点信息
        FlowNode currentNode = getFlowNode(bpmnModel, currentTask.getTaskDefinitionKey());

        // 保存当前节点的原出口连线
        List<SequenceFlow> currentNodeOutgoingFlows = new ArrayList<>(currentNode.getOutgoingFlows());

        // 为什么不生效，还没搞懂
//        SequenceFlow sequenceFlow = currentNodeOutgoingFlows.get(0);
//        sequenceFlow.setSourceRef(currentTask.getTaskDefinitionKey());
//        sequenceFlow.setTargetRef(bpmTaskApproveVo.getTargetActivityId());
//        List<SequenceFlow> targetNodeOutgoingFlows = new ArrayList<>();
//        targetNodeOutgoingFlows.add(sequenceFlow);
//        currentNode.setOutgoingFlows(targetNodeOutgoingFlows);

        // 将当前节点的出口连线设置为目标节点的所有出口
        String targetName = setOutgoingFlowsToTargetActivity(bpmnModel, currentNode, bpmTaskApproveVo.getTargetActivityId());

        System.out.println(currentNode);
        try {
            // 完成任务
            BpmTaskExt bpmTaskExt = new BpmTaskExt();
            bpmTaskExt.setId(currentTask.getId());
            bpmTaskExt.setName(currentTask.getName());
            bpmTaskExt.setStartTime(currentTask.getCreateTime());
            bpmTaskExt.setEndTime(currentTask.getClaimTime());
            bpmTaskExt.setProcessInstanceId(currentTask.getProcessInstanceId());
            bpmTaskExt.setExecutionId(currentTask.getExecutionId());
            bpmTaskExt.setProcessDefinitionId(currentTask.getProcessDefinitionId());
            bpmTaskExt.setAssigneeId(currentTask.getAssignee());
            String assigneeId = currentTask.getAssignee();
            if (assigneeId != null) {
                SysUser sysUser = userMapper.selectUserById(Long.valueOf(currentTask.getAssignee()));
                bpmTaskExt.setAssignee(sysUser == null ? null : sysUser.getNickName());
            }
            bpmTaskExt.setAssignee(currentTask.getAssignee());
            bpmTaskExt.setProcessDefinitionKey(currentTask.getTaskDefinitionKey());
            bpmTaskExt.setProcessDefinitionName(currentTask.getTaskDefinitionKey());

            bpmTaskExt.setFormFields(bpmTaskApproveVo.getFormFields());
            bpmTaskExt.setFormOption(bpmTaskApproveVo.getFormOption());
            bpmTaskExt.setFormVariables(bpmTaskApproveVo.getFormVariables());
            bpmTaskExt.setFormType(bpmTaskApproveVo.getFormType());
            bpmTaskExtMapper.insert(bpmTaskExt);

            Map<String, Object> variables = JSONUtil.parseObj(bpmTaskApproveVo.getFormVariables());
            taskService.setVariables(currentTask.getId(),variables);
            taskService.addComment(currentTask.getId(), currentTask.getProcessInstanceId(), TaskStatus.RETURNED.getTaskStatus(), "回退："+currentTask.getName()+"->"+targetName);
            taskService.complete(currentTask.getId(), taskService.getVariables(currentTask.getId()));

            //查询流程实例当前激活的节点
            Task nextTask = taskService.createTaskQuery()
                .processInstanceId(currentTask.getProcessInstanceId())
                .singleResult();
            taskService.setAssignee(nextTask.getId(),assignee);
        } finally {
            // 恢复当前节点的原出口连线
            currentNode.setOutgoingFlows(currentNodeOutgoingFlows);
        }
        return true;
    }

    /**
     * 获取流程节点
     *
     * @param bpmnModel   BPMN模型
     * @param activityId  活动ID
     * @return 流程节点
     */
    private FlowNode getFlowNode(BpmnModel bpmnModel, String activityId) {
        return (FlowNode) bpmnModel.getFlowElement(activityId);
    }

    /**
     * 将当前节点的出口连线设置为目标节点的所有出口连线
     *
     * @param bpmnModel         BPMN模型
     * @param sourceNode        源节点
     * @param targetActivityId  目标活动ID
     */
    private String setOutgoingFlowsToTargetActivity(BpmnModel bpmnModel, FlowNode sourceNode, String targetActivityId) {
        // 获取目标节点信息
        FlowNode targetNode = getFlowNode(bpmnModel, targetActivityId);

        // 获取目标节点的所有出口连线
        List<SequenceFlow> targetOutgoingFlows = targetNode.getOutgoingFlows();

        List<SequenceFlow> incomingFlows = targetNode.getIncomingFlows();


        // 去重并设置当前节点的出口为目标节点的所有出口
        List<SequenceFlow> uniqueTargetOutgoingFlows = incomingFlows.stream()
            .collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(sf -> sf.getTargetFlowElement().getId()))))
            .stream()
            .collect(Collectors.toList());

        sourceNode.setOutgoingFlows(uniqueTargetOutgoingFlows);

        return targetNode.getName();
    }



    public List<BpmTaskAssigneeVo> getNodeList(String taskId) {

        List<BpmTaskAssigneeVo> result = new ArrayList<>();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String processDefinitionId = task.getProcessDefinitionId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();

        // 获取 BpmnModel
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        // 获取流程定义中的所有流程元素
        Collection<FlowElement> flowElements = bpmnModel.getProcesses().get(0).getFlowElements();

        // 遍历流程元素，筛选出所有的用户任务节点
        for (FlowElement element : flowElements) {
            if (element instanceof UserTask && isPathExists(bpmnModel,element.getId(),task.getTaskDefinitionKey())) {
//                UserTask userTask = (UserTask) element;
                BpmTaskAssigneeVo bpmTaskAssigneeVo = new BpmTaskAssigneeVo();
                bpmTaskAssigneeVo.setProcessDefinitionId(processDefinitionId);
                bpmTaskAssigneeVo.setNodeId(element.getId());
                bpmTaskAssigneeVo.setNodeName(element.getName());
                bpmTaskAssigneeVo.setProcessDefinitionKey(processDefinition.getKey());
                result.add(bpmTaskAssigneeVo);
            }
        }
        return result;
    }


    /**
     * 判断是否存在从sourceNodeId到targetNodeId的路径
     * @param bpmnModel
     * @param sourceNodeId
     * @param targetNodeId
     * @return
     */
    private static boolean isPathExists(BpmnModel bpmnModel, String sourceNodeId, String targetNodeId) {

        // 获取所有流程元素
        List<FlowElement> flowElements = new ArrayList<>();
        bpmnModel.getProcesses().forEach(process -> flowElements.addAll(process.getFlowElements()));

        // 查找节点A和节点B
        FlowNode sourceNode = findFlowNode(flowElements, sourceNodeId);
        FlowNode targetNode = findFlowNode(flowElements, targetNodeId);

        // 判断是否存在路径
        return hasPath(sourceNode, targetNode);
    }

    private static FlowNode findFlowNode(List<FlowElement> flowElements, String nodeId) {
        for (FlowElement element : flowElements) {
            if (element instanceof FlowNode && element.getId().equals(nodeId)) {
                return (FlowNode) element;
            }
        }
        return null;
    }

    private static boolean hasPath(FlowNode sourceNode, FlowNode targetNode) {
        if (sourceNode == null || targetNode == null) {
            return false;
        }

        // 使用深度优先搜索（DFS）算法查找路径
        return hasPathDFS(sourceNode, targetNode, new ArrayList<>());
    }

    private static boolean hasPathDFS(FlowNode currentNode, FlowNode targetNode, List<String> visitedNodes) {
        if (currentNode.equals(targetNode)) {
            return true;
        }

        // 标记当前节点为已访问
        visitedNodes.add(currentNode.getId());

        // 遍历当前节点的出连线
        for (SequenceFlow outgoingFlow : currentNode.getOutgoingFlows()) {
            FlowNode nextNode = (FlowNode) outgoingFlow.getTargetFlowElement();

            // 如果下一个节点未被访问，递归调用DFS
            if (!visitedNodes.contains(nextNode.getId())) {
                if (hasPathDFS(nextNode, targetNode, visitedNodes)) {
                    return true;
                }
            }
        }

        // 如果遍历完所有出连线仍未找到目标节点，返回false
        return false;
    }

}
